perm filename DISPLY.FAI[S,NET] blob sn#874540 filedate 1989-06-15 generic text, type C, neo UTF8
COMMENT ⊗   VALID 00026 PAGES
C REC  PAGE   DESCRIPTION
C00001 00001
C00005 00002	 SUBTTL Display routines, intended to be .INSERTed  history NLINES DMLINL DDLINL LINEL DMBUFL NCHARS NWRDLN SCRSIZ NGW DISLIN DMLIN DDDLIN TRUNCA NOEEOL NOEEOB
C00010 00003	 Data area  DPCBEG DDP IIIP DMP HSIZE VSIZE OVPOS WRAPPD SLUPDP SCUPDP SAUPDP CRUPDP SCRLCT DMCNT DMPNT DPCEND
C00012 00004	 Display commands and programs  IIIHDR SDISP SCP DP2BEG SCREEN SCREND SCPL BOTLIN VPOS HPOS ROLLP INSDLP CRP LSTATE SCREE1 SCREE2 DP2END FREELF CDISP SCC SCCL LDISP LNP DMDISP DMPGM
C00015 00005	 DPYINI -- Initialize display package  DPYINI NOTIII
C00017 00006	 CLRSCN -- Clear display screen (does explicit update as well)  CLRSCN SCRIN1 DMCEOF
C00019 00007	 SCSTOR -- Store a character at the current screen V/H position  SCSTOR SCSTO2 SCBYTP
C00021 00008	 CLREOL -- Clear to end of line  CLREOL CLEOL0 CLEOL1
C00023 00009	 CLREOF -- Clear to end of screen  CLREOF CLEOF1
C00024 00010	 CLR1CH -- Delete a character at the cursor  CLR1CH CLR1C0
C00025 00011	 CSRPOS -- position cursor  CSRPOS ADHPOS ADHPO2
C00027 00012	 TERPRI -- CR, LF, CLEOL, scroll if necessary  TERPRI
C00028 00013	 CARRET -- CR  CARRET
C00029 00014	 LINEFD -- LF, CLEOL, scroll if necessary  LINEFD LINFD1 LINFD2
C00031 00015	 LINSRV -- Line starve  LINSRV LINSR1
C00032 00016	 CSRTAB -- Tab cursor  CSRTAB CSRTA0
C00033 00017	 CSRAOS -- Forespace cursor  CSRAOS CSRAO0
C00034 00018	 CSRSOS -- Backspace cursor  CSRSOS CSRSO0
C00035 00019	 INSLIN -- Insert a blank line  INSLIN INSLN0 INSL1A INSLN1 INSLN2
C00037 00020	 DELLIN -- Delete a blank line  DELLIN DELLN0 DELL1A DELLN1
C00039 00021	 INSCHR -- Insert a blank character  INSCHR INSCH0 INSC1A INSCH2 INSCH1
C00042 00022	 DELCHR -- Delete a character  DELCHR DELCH0 DELC1A DELCH2 DELCH1
C00045 00023	 SCNUPD -- Update screen  SCNUPD CSRUPD CSRNOE SCUPD1 SCUP1A SCUPD2 CSRER0 CSRERS
C00050 00024	 Save and restore whole screen  SCSAV1 SCSAV2 SCSAV3 SCSA12 SCSA21 SCRES1 SCRES2
C00052 00025	 GETCSY UPDLIN Redisplay subroutines  GETCSY UPDLIN
C00054 00026	 DMCHAR DMOUT Datamedia output routines  DMCHAR DMOUT
C00056 ENDMK
C⊗;
 SUBTTL Display routines, intended to be .INSERTed ;⊗ history NLINES DMLINL DDLINL LINEL DMBUFL NCHARS NWRDLN SCRSIZ NGW DISLIN DMLIN DDDLIN TRUNCA NOEEOL NOEEOB

;This is a library of display hacking routines.  Each routine documents its
;calling sequence and what AC's it smashes.  Only AC's 0-6 are used.  A
;pushdown stack is expected in 17.

;Original implementation by Mark Crispin, SU-AI, July 1978

COMMENT ⊗  History (please record changes):

26 Feb 84  JJW	Conversion from MIDAS to FAIL.
28 May 86  ME	Put in tests for HPOS overflowing buffers, to avoid clobbering
		things outside SCREEN array with characters.  Also, SCSTOR
		fixed to wrap around automatically (with CR and LF) if attempt
		to store character beyond right margin (HSIZE).
11 Sep 87  JJW	Changed CAMGE 2,HSIZE to CAMG at SCSTOR+1 since HSIZE is one
		less than the number of columns.
03 Dec 88  ME	Improved CSRUPD routine, simultaneously allowing wider cursor.
		Display tiny cursor at beginning of line that cursor is on.
		Added one to NGW to avoid clobbering DD execute at end of SCC.
10 Dec 88  ME	Fixed wrapping around to set cursor position to next line
		immediately so that display commands from there won't be
		ignored for being beyond the end of the previous line.
11 Dec 88  ME	Flushed silly SOS'ing of HSIZE -- store true horiz width.
		Fixed various comparisons against HSIZE.
27 Jan 89  ME	Added routines to save and restore the screen state
		(text, cursor position, roll mode, i/d, CR mode), with
		two separate copies.  See SCSAV1.

history:  end of comment ⊗ 

	BEGIN DISPLY

; Useful definitions

NLINES←←=40				; lines in screen matrix
LINEL←←=88				; true maximum number of characters/line
DMBUFL←←=100				; size of DM buffer
NCHARS←←<<<LINEL+5>/5>*5>		; characters/line in line array
NWRDLN←←4+NCHARS/5			; number of words in line array
SCRSIZ←←NLINES*NWRDLN			; number of words in screen matrix
NGW←←1+<<<NCHARS*3>+=17>/=16>		; number of graphics words

; Line characteristics bits

DISLIN←←<400000,,0>			; III
DMLIN←← <040000,,0>			; DM
DDDLIN←←<020000,,0>			; DD

; DM UPGIOT flags

TRUNCA←←<040000,,0>			; truncate output lines to =80 characters
NOEEOL←←<020000,,0>			; suppress CEOL when moving to a line
NOEEOB←←<010000,,0>			; suppress CEOL on blank line

; Generate a Data Disc/III command

DEFINE DDCMD(O1,D1,O2,D2,O3,D3)<
 BYTE (8)D1,D2,D3(3)O1,O2,O3,4
>;DEFINE

; Insert a command in a DM display program

DEFINE DMCMD(CH)<
 MOVE DMCNT
 CAIGE 10
  PUSHJ 17,DMOUT
 MOVEI 177
 PUSHJ 17,DMCHAR
 MOVEI CH
 PUSHJ 17,DMCHAR
>;DEFINE
; Data area ;⊗ DPCBEG DDP IIIP DMP HSIZE VSIZE OVPOS WRAPPD SLUPDP SCUPDP SAUPDP CRUPDP SCRLCT DMCNT DMPNT DPCEND

↑DPCBEG←←.			; beginning of core area zeroed at DPYINI

; Terminal characteristics

DDP:	BLOCK 1			; -1 → Data Disc
IIIP:	BLOCK 1			; -1 → III
DMP:	BLOCK 1			; -1 → Datamedia
↑HSIZE:	BLOCK 1			; horizontal size
↑VSIZE:	BLOCK 1			; vertical size

; Cursor position pointers

OVPOS:	BLOCK 1			; old vertical position
WRAPPD:	BLOCK 1			; -1 if just wrapped around, 0,,-1 after CR

; Screen updating flags

SLUPDP:	BLOCK NLINES		; -1 → this line has changed
SCUPDP:	BLOCK 1			; -1 → some update happened someplace
SAUPDP:	BLOCK 1			; -1 → updated whole screen
CRUPDP:	BLOCK 1			; -1 → updated cursor
SCRLCT:	BLOCK 1			; for forcing update in LINEFD scrolling

; DM display fields

DMCNT:	BLOCK 1			; DM program counter
DMPNT:	BLOCK 1			; DM program counter

DPCEND←←.-1			; end of core data
; Display commands and programs ;⊗ IIIHDR SDISP SCP DP2BEG SCREEN SCREND SCPL BOTLIN VPOS HPOS ROLLP INSDLP CRP LSTATE SCREE1 SCREE2 DP2END FREELF CDISP SCC SCCL LDISP LNP DMDISP DMPGM

IIIHDR:	BYTE (11)<-777>,640(3)4,2(2)1,2(4)6

SDISP:	600000,,SCP ↔ SCPL ↔ 0 ↔ SCP	; display screen
SCP:	DDCMD 1,46,4,1,5,10
	DDCMD 3,2,3,2,3,2
DP2BEG←←.			; another area cleared at DPYINI
SCREEN:	BLOCK SCRSIZ
SCREND←.-1
	0
SCPL←←.-SCP
BOTLIN←←SCREEN+SCRSIZ-NWRDLN+2-1
↑VPOS:	BLOCK 1			; vertical position
↑HPOS:	BLOCK 1			; horizontal position
↑ROLLP:	BLOCK 1			; -1 → scrolling permission
↑INSDLP:BLOCK 1			;-1 → insert/delete mode on
↑CRP:	BLOCK 1			;-1 → last character CR, ignore LF
LSTATE←←.-SCREEN		; length of array describing screen state

SCREE1:	BLOCK LSTATE		; first saved state of screen
SCREE2:	BLOCK LSTATE		; second saved state of screen
DP2END←←.-1			; end of area cleared at DPYINI

↑FREELF:BLOCK 1			;-1 → invent LF after CR, ignore real LF there

CDISP:	SCC ↔ SCCL ↔ 0 ↔ SCC+1		; display cursor
SCC:	DDCMD 1,7,1,7,1,7
	DDCMD 3,1,4,0,5,0
	BLOCK NGW
	DDCMD 0,0,1,46,1,46
	0
SCCL←←.-SCC

LDISP:	200000,,LNP ↔ NWRDLN ↔ 0 ↔ LNP	; display line
LNP:	DDCMD 1,46,4,0,5,0
	DDCMD 3,2,3,2,3,2
	BLOCK NWRDLN-3
	0

DMDISP:	TRUNCA!NOEEOL!NOEEOB+DMPGM ↔ 0 ↔ 0; DM display program
DMPGM:	BLOCK DMBUFL
; DPYINI -- Initialize display package ;⊗ DPYINI NOTIII
; Call:	PUSHJ 17,DPYINI
;	<return>
; Smashes 0.

↑DPYRDW:SETOM SAUPDP		;force text to be redrawn
	SETOM CRUPDP		;force cursor to be redrawn
	PPACT
	LEYPOS 2000			; flush page printer and line editor
	POPJ 17,

↑DPYINI:SETZM DPCBEG
	MOVE [DPCBEG,,DPCBEG+1]
	BLT DPCEND			; initialize data area
	SETZM DP2BEG
	MOVE [DP2BEG,,DP2BEG+1]
	BLT DP2END			; initialize display area
	SETOM FREELF			; insert LF after CR
	HRROI [3000,,]			; real line chars
	TTYSET				; get my line characteristics
	TLNN (DISLIN!DDDLIN!DMLIN)	; display
	 JRST [	OUTSTR [ASCIZ/Not a display!/]
		EXIT]
	TLNE (DISLIN)
	 SETOM IIIP
	TLNE (DDDLIN)
	 SETOM DDP
	TLNE (DMLIN)
	 SETOM DMP
	MOVE [-2,,[6000,,HSIZE ↔ 15000,,VSIZE]]
	TTYSET				; get screen size information
	MOVNI 2 ↔ ADDM VSIZE
;;	SOS HSIZE
	PPACT
	LEYPOS 2000			; flush page printer and line editor
	MOVE VSIZE
	MOVEM SCRLCT
	SETOM ROLLP			; grant scrolling permission
	SKIPN IIIP
	 JRST NOTIII
	SETZM SCP			; different header for III's
	MOVE IIIHDR
	MOVEM SCP+1
NOTIII:	SETOM OVPOS
	SKIPN DMP
	 POPJ 17,
	MOVEI <5*DMBUFL>-4
	MOVEM DMCNT			; initialize DM counter
	MOVE [440700,,DMPGM]
	MOVEM DMPNT			; initialize DM pointer
	POPJ 17,
; CLRSCN -- Clear display screen (does explicit update as well) ;⊗ CLRSCN SCRIN1 DMCEOF
; Call:	PUSHJ 17,CLRSCN
;	<return>
; Smashes 0, 1, 2, and 3.

↑CLRSCN:SETZM VPOS
	SETZM HPOS
	SETZM WRAPPD			; haven't wrapped around
	MOVE [<ASCII/     />+1]
	MOVEM SCREEN
	MOVE [SCREEN,,SCREEN+1]
	BLT SCREND			; initialize screen to spaces
	MOVE [<ASCII/
/>+1]					; terpri
	SETZ 1,				; line address
	DMOVE 2,[1 ↔ NLINES]		; blank word/line counter
SCRIN1:	MOVEM 2,SCREEN(1)
	MOVEM 2,SCREEN+1(1)
	MOVEM SCREEN+NWRDLN-2(1)
	MOVEM 2,SCREEN+NWRDLN-1(1)
	ADDI 1,NWRDLN
	SETZM SLUPDP-1(3)		; clear update necessary state for line
	SOJG 3,SCRIN1
	MOVEI 2
	MOVEM SCC+2
	MOVE [SCC+2,,SCC+3]
	BLT SCC+2+NGW-1			; initialize graphics words
	SETOM SAUPDP			; updated entire screen
	PUSHJ 17,SCNUPD
	SKIPN DMP
	 POPJ 17,

	DMCMD 14			; cursor ← [0,2]
	MOVEI 140
	PUSHJ 17,DMCHAR
	MOVEI 142
	PUSHJ 17,DMCHAR
	MOVE 1,VSIZE
DMCEOF:	DMCMD 27			; clear current line
	DMCMD 15			; move to next line
	SOJG 1,DMCEOF
	DMCMD 14			; cursor ← [0,2]
	MOVEI 140
	PUSHJ 17,DMCHAR
	MOVEI 142
	PUSHJ 17,DMCHAR
	JRST DMOUT
; SCSTOR -- Store a character at the current screen V/H position ;⊗ SCSTOR SCSTO2 SCBYTP
; Call:	MOVE <character>
;	PUSHJ 17,SCSTOR
;	<return>
; Smashes 0, 1, 2, and 3.

↑SCSTOR:MOVE 2,HPOS
	CAMGE 2,HSIZE		; this should no longer ever skip
	JRST SCSTO2
	PUSH 17,0		; save char to be stored
	PUSHJ 17,CARRET		; wrap around with CR
	PUSHJ 17,LINEFD		; and LF
	POP 17,0
	MOVE 2,HPOS		; get new position
SCSTO2:	MOVE 1,VPOS
	SETOM SLUPDP(1)		; updated flags
	SETOM SCUPDP
	IMULI 1,NWRDLN
	IDIVI 2,5		; word position in line
	ADDI 1,SCREEN+2(2)	; offset by lines previous
	DPB 0,SCBYTP(3)		; save character on screen
	PUSHJ 17,ADHPOS		; advance HPOS and check for wrap around
	SKIPE DMP
	 JRST DMCHAR
	POPJ 17,

; Byte pointer table for insertions

SCBYTP:	350700,,(1)
	260700,,(1)
	170700,,(1)
	100700,,(1)
	010700,,(1)
; CLREOL -- Clear to end of line ;⊗ CLREOL CLEOL0 CLEOL1
; Call:	PUSHJ 17,CLREOL
;	<return>
; Smashes 0, 1, 2, 3, and 4.
; (On DM3025, this doesn't clear "wrapped-around", so we don't clear it here.)

↑CLREOL:SKIPN DMP
	 JRST CLEOL0
	DMCMD 27			; clear to end of line
CLEOL0:	MOVE 4,HPOS
	CAIL 4,LINEL
	 POPJ 17,
	MOVE 1,VPOS
	SETOM SLUPDP(1)			; flag this line changed
	SETOM SCUPDP
	IMULI 1,NWRDLN
	MOVE 2,HPOS
	IDIVI 2,5
	ADDI 1,SCREEN+2(2)		; address of word to hack
	MOVE 2,SCBYTP(3)
	MOVEI " "
	DPB 2				; clear character
CLEOL1:	ADDI 4,1			; next character position
	CAIL 4,LINEL
	 POPJ 17,
	IDPB 2
	JRST CLEOL1
; CLREOF -- Clear to end of screen ;⊗ CLREOF CLEOF1
; Call:	PUSHJ 17,CLREOF
;	<return>
; Smashes 0, 1, 2, 3, and 4.

↑CLREOF:PUSH 17,HPOS
	PUSH 17,VPOS
CLEOF1:	PUSHJ 17,CLREOL			; clear to end of line
	DMCMD 15			; next line
	SETZM HPOS
	AOS 1,VPOS
	CAIGE 1,NLINES
	 JRST CLEOF1
	POP 17,VPOS
	POP 17,HPOS
	DMCMD 14			; cursor ← [0,2]
	MOVEI 140
	PUSHJ 17,DMCHAR
	MOVEI 142
	PUSHJ 17,DMCHAR
	POPJ 17,
; CLR1CH -- Delete a character at the cursor ;⊗ CLR1CH CLR1C0
; Call:	PUSHJ 17,CLR1CH
;	<return>
; Smashes 0, 1, 2, and 3.

↑CLR1CH:SKIPN DMP
	 JRST CLR1C0
	DMCMD " "		; space then backspace
	DMCMD 10
CLR1C0:	MOVEI " "		; store space at cursor
	MOVE 1,VPOS
	SETOM SLUPDP(1)
	SETOM SCUPDP
	IMULI 1,NWRDLN
	SETZM WRAPPD		; forget about any previous wrap around
	MOVE 2,HPOS
	CAIL 2,LINEL		; within valid range?
	POPJ 17,		; no
	IDIVI 2,5
	ADDI 1,SCREEN+2(2)
	DPB SCBYTP(3)
	POPJ 17,
; CSRPOS -- position cursor ;⊗ CSRPOS ADHPOS ADHPO2
; Call:	MOVE [xpos,,ypos]
;	PUSHJ 17,CSRPOS
;	<return>
; Smashes 0.

↑CSRPOS:HLRZM HPOS
	HRRZM VPOS
	HLRZ 0,0		;just the horizontal position
	CAML 0,HSIZE		;off right edge of screen?
	SETZM HPOS		;yes, (some) real DMs go to col 0 in this case
	SETZM WRAPPD		;forget about any previous wrap around
	SETOM CRUPDP
	SKIPN DMP
	 POPJ 17,
	DMCMD 14
	MOVE HPOS
	XORI 140
	PUSHJ 17,DMCHAR
	MOVE VPOS
	ADDI 2
	XORI 140
	JRST DMCHAR

;Advance cursor position by one, check for wrap around.
;Smashes only AC 0.
ADHPOS:	AOS HPOS		;advance cursor position
	MOVE 0,HPOS		;get new value
ADHPO2:	SETZM WRAPPD		;assume haven't wrapped around now
	CAMGE 0,HSIZE		;off edge of screen
	POPJ 17,		;no
	PUSHJ 17,CARRET		;wrap around with CR
	PUSH 17,1
	PUSH 17,2
	PUSH 17,3
	PUSH 17,4
	PUSHJ 17,LINEFD		;and LF
	POP 17,4
	POP 17,3
	POP 17,2
	POP 17,1
	SETOM WRAPPD		;remember we wrapped around
	POPJ 17,
; TERPRI -- CR, LF, CLEOL, scroll if necessary ;⊗ TERPRI
; Call:	PUSHJ 17,TERPRI
;	<return>
; Smashes 0, 1, 2, 3, and 4.

↑TERPRI:PUSHJ 17,CARRET
	PUSHJ 17,LINEFD
	JRST CLREOL
; CARRET -- CR ;⊗ CARRET
; Call:	PUSHJ 17,CARRET
;	<return>
; Smashes 0.

↑CARRET:SKIPN WRAPPD		; recently wrap around from end of line?
	JRST CARRE2		; no
	SKIPN HPOS		; redundant check for having just wrapped
	SKIPL WRAPPD		; skip unless we had already seen a CR
	SETZM WRAPPD		; forget that we have wrapped around
	HRRZS WRAPPD		; we've seen at least a CR
CARRE2:	MOVE 0,VPOS		; move to [0,current y]
	JRST CSRPOS
; LINEFD -- LF, CLEOL, scroll if necessary ;⊗ LINEFD LINFD1 LINFD2
; Call:	PUSHJ 17,CARRET
;	<return>
; Smashes 0, 1, 2, 3, and 4.

↑LINEFD:SKIPE WRAPPD		;recently wrapped around from end of line?
	JRST LINFD2		;yes, ignore this LF
	AOS 1,VPOS		;move to next line
	CAMGE 1,VSIZE
	 JRST LINFD1
	SKIPN ROLLP
	 JRST [	SETZM VPOS ↔ JRST LINFD1]
	MOVE [SCREEN+NWRDLN,,SCREEN]
	BLT SCREEN+SCRSIZ-NWRDLN-1
	MOVE [<ASCII/     />+1]
	MOVEM BOTLIN+1
	MOVE [BOTLIN+1,,BOTLIN+2]
	BLT BOTLIN+<NCHARS/5>
	SETOM SAUPDP
	MOVE VSIZE ↔ SOS ↔ MOVEM VPOS
	SOSG SCRLCT
	 PUSHJ 17,SCNUPD		; update after a screenfull of scrolling
LINFD1:	SETOM CRUPDP
	POPJ 17,

LINFD2:	SETZM WRAPPD		;forget that we had wrapped around
	SKIPN HPOS		;redundant check to validate having just wrapped
	POPJ 17,
	JRST LINEFD		;oops, we forgot to clear WRAPPD somewhere!
; LINSRV -- Line starve ;⊗ LINSRV LINSR1
; Call:	PUSHJ 17,LINSRV
;	<return>
; Smashes 0.

↑LINSRV:SKIPN DMP
	 JRST LINSR1
	DMCMD 32
LINSR1:	SOSGE VPOS
	 SETZM VPOS
	SETZM WRAPPD		;forget about any previous wrap around
	SETOM CRUPDP
	POPJ 17,
; CSRTAB -- Tab cursor ;⊗ CSRTAB CSRTA0
; Call:	PUSHJ 17,CSRTAB
;	<return>
; Smashes 0.

↑CSRTAB:SKIPE DMP
	 JRST CSRTA0
	DMCMD 11		; tab
CSRTA0:	MOVE 0,HPOS
	TRZ 0,7
	ADDI 0,10
	MOVEM 0,HPOS
	PUSHJ 17,ADHPO2		; check for wrap around
	SETOM CRUPDP		; cursor updated
	POPJ 17,
; CSRAOS -- Forespace cursor ;⊗ CSRAOS CSRAO0
; Call:	PUSHJ 17,CSRAOS
;	<return>
; Smashes 0.

↑CSRAOS:SKIPE DMP
	 JRST CSRAO0
	DMCMD 34		; forespace
CSRAO0:	PUSHJ 17,ADHPOS		; increment HPOS and check for wrap around
	SETOM CRUPDP		; cursor updated
	POPJ 17,
; CSRSOS -- Backspace cursor ;⊗ CSRSOS CSRSO0
; Call:	PUSHJ 17,CSRSOS
;	<return>
; Smashes 0.

↑CSRSOS:SKIPE DMP
	 JRST CSRSO0
	DMCMD 10		; backspace
CSRSO0:	SOSGE HPOS
	 SETZM HPOS
	SETZM WRAPPD		; forget about any previous wrap around
	SETOM CRUPDP		; cursor updated
	POPJ 17,
; INSLIN -- Insert a blank line ;⊗ INSLIN INSLN0 INSL1A INSLN1 INSLN2
; Call:	MOVE <number of lines to insert>
;	PUSHJ 17,INSLIN
;	<return>
; Smashes 0, 1, 2, and 3.

↑INSLIN:PUSH 17,0			; save argument
	SETZM WRAPPD			; forget about any previous wrap around
	SKIPN DMP
	 JRST INSLN0
	DMCMD 20			; enter i/d mode on DM
INSLN0:	SKIPN DMP
	 JRST INSL1A
	DMCMD 12			; insert a line on DM
INSL1A:	MOVE 3,VPOS
	IMULI 3,NWRDLN
	ADDI 3,SCREEN			; address of first word of cursor line
	CAIN 3,SCREEN+<NLINES-1>*NWRDLN	; skip unless at bottom line
	 JRST INSLN2
	MOVE 1,[SCREEN+<NLINES-2>*NWRDLN,,SCREEN+<NLINES-1>*NWRDLN]
INSLN1:	MOVE 2,1
	BLT 2,NWRDLN-1(1)		; move line down
	ADJSP 1,-NWRDLN
	CAIE 3,(1)
	 JRST INSLN1
INSLN2:	MOVE 1,[<ASCII/     />+1]
	MOVEM 1,2(3)
	MOVEI 1,NWRDLN-3(3)
	ADDI 3,3
	HRLI 3,-1(3)
	BLT 3,(1)
	SOSLE (17)
	 JRST INSLN0
	ADJSP 17,-1			; flush argument
	SETOM SAUPDP
	SKIPN DMP
	 POPJ 17,
	DMCMD 30			; leave i/d mode on DM
	POPJ 17,
; DELLIN -- Delete a blank line ;⊗ DELLIN DELLN0 DELL1A DELLN1
; Call:	MOVE <number of lines to delete>
;	PUSHJ 17,DELLIN
;	<return>
; Smashes 0, 1, 2, and 3.

↑DELLIN:PUSH 17,			; save argument
	SETZM WRAPPD			; forget about any previous wrap around
	SKIPN DMP
	 JRST DELLN0
	DMCMD 20			; enter i/d mode on DM
DELLN0:	SKIPN DMP
	 JRST DELL1A
	DMCMD 32			; delete a line on DM
DELL1A:	MOVE 3,VPOS
	IMULI 3,NWRDLN
	ADDI 3,SCREEN			; address of first word of cursor line
	CAIN 3,SCREEN+<NLINES-1>*NWRDLN	; skip unless at bottom line?
	 JRST DELLN1
	MOVEI 1,(3)
	ADDI 1,NWRDLN
	HRLI 3,(1)
	BLT 3,SCREEN+<NLINES-1>*NWRDLN-1; move the lines up
DELLN1:	MOVE 1,[<ASCII/     />+1]
	MOVEM 1,2(3)
	MOVEI 1,NWRDLN-3(3)
	ADDI 3,3
	HRLI 3,-1(3)
	BLT 3,(1)
	SOSLE (17)
	 JRST DELLN0
	ADJSP 17,-1			; flush argument
	SETOM SAUPDP
	SKIPN DMP
	 POPJ 17,
	DMCMD 30			; leave i/d mode on DM
	POPJ 17,
; INSCHR -- Insert a blank character ;⊗ INSCHR INSCH0 INSC1A INSCH2 INSCH1
; Call:	MOVE <number of spaces to insert>
;	PUSHJ 17,INSCHR
;	<return>
; Smashes 0, 1, 2, 3, 4, 5, and 6.

↑INSCHR:PUSH 17,			; save count
	SETZM WRAPPD			; forget about any previous wrap around
	SKIPN DMP			; is this a DM?
	 JRST INSCH0
	DMCMD 20			; yes, enter i/d mode
INSCH0: SKIPN DMP			; on a DM?
	 JRST INSC1A
	DMCMD 34			; insert a character
INSC1A:	MOVE 1,VPOS			; get vertical position
	IMULI 1,NWRDLN			; now number of words
	MOVE 4,1			; copy it for hacking
	ADDI 4,SCREEN+NWRDLN-3		; address of last text word
	MOVE 2,HPOS			; get horizontal position
	CAIL 2,LINEL			; within valid range?
	JRST [	ADJSP 17,-1		; no
		POPJ 17,]
	IDIVI 2,5			; make it words
	ADDI 1,SCREEN+2(2)		; address of word with cursor
	LDB 2,[010700,,(1)]		; first character in next word
	LDB 5,[	103400,,(1)
		102500,,(1)
		101600,,(1)
		100700,,(1)
		100000,,(1)](3)
	DPB 5,[	013400,,(1)
		012500,,(1)
		011600,,(1)
		010700,,(1)
		010000,,(1)](3)
	MOVEI 5," "			; space in hole
	DPB 5,[	350700,,(1)
		260700,,(1)
		170700,,(1)
		100700,,(1)
		010700,,(1)](3)
	JRST INSCH1			; check for being done

; At each iteration Y has last character, X has next address

INSCH2:	MOVE 3,2			; copy the character
	LDB 2,[010700,,(1)]		; first character in next word
	DPB 3,[000700,,(1)]		; last character here
	MOVE 3,(1)			; get word being hacked
	ROT 3,-7			; put characters in right place
	IORI 3,1			; make sure bit 1.1 is on
	MOVEM 3,(1)			; save character in word
INSCH1:	CAME 1,4			; at last address?
	 AOJA 1,INSCH2
	SETOM SCUPDP			; some update somewhere
	MOVE 1,VPOS			; this line
	SOSLE (17)
	 JRST INSCH0			; loop for more characters
	ADJSP 17,-1
	SETOM SLUPDP(1)			; this line was hacked
	SKIPN DMP			; on a DM?
	 POPJ 17,
	DMCMD 30			; leave i/d mode
	POPJ 17,
; DELCHR -- Delete a character ;⊗ DELCHR DELCH0 DELC1A DELCH2 DELCH1
; Call:	MOVE <number of characters to delete>
;	PUSHJ 17,DELCHR
;	<return>
; Smashes 0, 1, 2, 3, 4, 5, and 6.

↑DELCHR:PUSH 17,			; save argument
	SETZM WRAPPD			; forget about any previous wrap around
	SKIPN DMP			; is this a DM?
	 JRST DELCH0
	DMCMD 20			; yes, enter i/d mode
DELCH0:	SKIPN DMP			; on a DM?
	 JRST DELC1A
	DMCMD 10			; delete a character
DELC1A:	MOVE 1,VPOS			; get current vertical position
	IMULI 1,NWRDLN			; number of words
	MOVE 4,1			; save it for later
	ADDI 4,SCREEN+NWRDLN-3		; address of last text word in line
	MOVE 2,HPOS			; get horizontal position
	CAIL 2,LINEL			; within valid range?
	JRST [	ADJSP 17,-1		; no
		POPJ 17,]
	IDIVI 2,5			; number of words
	ADDI 1,SCREEN+2(2)		; address of word with cursor
	LDB 5,[	013400,,(1)
		012500,,(1)
		011600,,(1)
		010700,,(1)
		010000,,(1)](3)
	DPB 5,[	103400,,(1)
		102500,,(1)
		101600,,(1)
		100700,,(1)
		100000,,(1)](3)
	JRST DELCH1			; check for being done

; Each time around the iteration A had address of next word

DELCH2:	LDB 2,[350700,,(1)]		; last character in previous
	DPB 2,[010700,,-1(1)]		; to previous
	LDB 2,[013400,,(1)]		; get last characters in this word
	DPB 2,[103400,,(1)]		; put back left justified
DELCH1:	CAME 1,4			; done?
	 AOJA 1,DELCH2
	MOVEI 2," "			; get a space
	DPB 2,[010700,,(1)]		; blank out last column
	SETOM SCUPDP			; screen updated someplace
	MOVE 1,VPOS			; get this line
	SOSLE (17)
	 JRST DELCH0			; hack another character
	ADJSP 17,-1
	SETOM SLUPDP(1)			; flag this line hacked
	SKIPN DMP			; on a DM?
	 POPJ 17,
	DMCMD 30			; leave i/d mode
	POPJ 17,
; SCNUPD -- Update screen ;⊗ SCNUPD CSRUPD CSRNOE SCUPD1 SCUP1A SCUPD2 CSRER0 CSRERS
; Call:	PUSHJ 17,SCNUPD
;	<return>
; Smashes 0, 1, 2, 3, and 4.

↑SCNUPD:MOVSI 1,-NLINES
	SETZ 2,
	SKIPE SLUPDP(1)			; does this line need hacking?
	 ADDI 2,1
	AOBJN 1,.-2
	SKIPN IIIP			; III always updates everything
	 CAIL 2,4			; after four lines update entire screen
	  SETOM SAUPDP
	MOVE VSIZE
	MOVEM SCRLCT
	AOSE SAUPDP			; update entire screen?
	 JRST SCUPD1
	SETZM SCUPDP			; clear other update flags
	SETZM SLUPDP
	MOVE [SLUPDP,,SLUPDP+1]
	BLT SLUPDP+NLINES-1
	SKIPE DMP			; is this a DM?
	 JRST [	PUSHJ 17,DMOUT
		JRST CSRUPD]
	UPGIOT SDISP			; output new screen on DD and III

; Update cursor

CSRUPD:	SKIPE DMP			; is this a DM?
	 JRST [	DMCMD 14		; cursor ← [0,2]
		MOVE HPOS
		XORI 140
		PUSHJ 17,DMCHAR
		MOVE VPOS
		ADDI 2
		XORI 140
		PUSHJ 17,DMCHAR
		JRST DMOUT]
	SKIPN DDP			; no cursor stuff yet for III's
	 POPJ 17,
	MOVEI 0,2
	MOVEM 0,SCC+2
	MOVE 0,[SCC+2,,SCC+3]
	BLT 0,SCC+2+NGW-1		; initialize graphics words
	SKIPL 1,OVPOS			; got an old position?
	 CAMN 1,VPOS			; yes, different from new position?
	  JRST CSRNOE			; no or no, don't need to erase cursor
	PUSHJ 17,CSRERS			; erase cursor
CSRNOE:	MOVE 1,HPOS			; horizontal character position
	CAIL 1,LINEL			; within valid range?
	JRST CSRER0			; no, just erase old cursor if any
	IMULI 1,6			; horizontal bit position
	ADDI 1,2			; graphics mode hack
	IDIVI 1,=32			; 32 bits/graphics word, get start word
	MOVNS 2
repeat 0,< ; old slow code, doesn't immediately allow cursor bigger than 4 bits
	MOVSI 3,740000			; 4-bit cursor
	LSH 3,(2)
	LDB 4,[010300,,3]
	ROT 4,-3
	TRZ 3,17
>;repeat 0
repeat 1,< ; new speedy, general code (any size cursor)
	MOVSI 3,760000			; make cursor 5 bits wide
	MOVEI 4,0
	LSHC 3,-4(2)			; shift low four bits into second word
	LSH 3,4				; realign high 32 bits in first word
>;repeat 1
	IORI 3,2			; make two DD graphics words
	IORI 4,2
	DMOVEM 3,SCC+2(1)
	MOVSI 1,700000			; start cursor line with tiny cursor
	IORM 1,SCC+2			;   in left margin
	MOVE 1,VPOS			; save vertical position
	MOVEM 1,OVPOS
	PUSHJ 17,GETCSY
	DDUPG CDISP			; draw new cursor
	POPJ 17,

SCUPD1:	AOSE SCUPDP			; did any update happen?
	 JRST SCUPD2
	MOVSI 1,-NLINES
SCUP1A:	SKIPE SLUPDP(1)			; need to hack this line?
	 PUSHJ 17,UPDLIN
	AOBJN 1,SCUP1A
	JRST CSRUPD

SCUPD2:	AOSE CRUPDP			; was cursor hacked?
	 POPJ 17,
	JRST CSRUPD

;Erase old cursor, if any.
CSRER0:	SKIPGE 1,OVPOS			; skip if any old cursor
	POPJ 17,			; nothing to erase
CSRERS:	PUSHJ 17,GETCSY			; get cursor vertical position
	DDUPG CDISP			; erase old cursor
	SETOM OVPOS			; no cursor now
	POPJ 17,
; Save and restore whole screen ;⊗ SCSAV1 SCSAV2 SCSAV3 SCSA12 SCSA21 SCRES1 SCRES2

; Save a copy of the screen image in core
; Smashes 1 and 2.

↑SCSAV1:SKIPA 1,[SCREEN,,SCREE1]	;save screen in primary copy
↑SCSAV2:MOVE 1,[SCREEN,,SCREE2]		;save screen in secondary copy
SCSAV3:	MOVEI 2,(1)
	BLT 1,LSTATE-1(2)		;save a copy of text on screen
	POPJ 17,

↑SCSA12:MOVE 1,[SCREE1,,SCREE2]		;save primary copy in secondary copy
	JRST SCSAV3

↑SCSA21:MOVE 1,[SCREE2,,SCREE1]		;save secondary copy in primary copy
	JRST SCSAV3

; Restore a copy of the screen image from saved copy in core
; Smashes 1.

↑SCRES1:SKIPA 1,[SCREE1,,SCREEN]	;restore primary copy
↑SCRES2:MOVE 1,[SCREE2,,SCREEN]		;restore secondary copy
	BLT 1,SCREEN+LSTATE-1		;restore a saved copy of screen text
	SETOM SAUPDP			;force screen update
	SETOM CRUPDP			;force cursor update
	POPJ 17,
; GETCSY UPDLIN Redisplay subroutines ;⊗ GETCSY UPDLIN

; Set up display program vertical position

GETCSY:	IMULI 1,=12
	ADDI 1,=24+=10
	DPB 1,[140400,,SCC+1]
	LSH 1,-4
	DPB 1,[240500,,SCC+1]
	POPJ 17,

; Display a single line

UPDLIN:	SKIPE DMP
	 POPJ 17,
	SKIPE LDISP+2
	 UPGIOT [0 ↔ 0 ↔ 0 ↔ 0]		; wait for previous
	SETZM SLUPDP(1)
	HRRZ 2,1			; line number
	IMULI 2,NWRDLN			; word position
	MOVSI 2,SCREEN+2(2)		; address of start of line
	HRRI 2,LNP+2
	BLT 2,LNP+NWRDLN-2
	HRRZ 2,1			; get line number again
	IMULI 2,=12
	ADDI 2,=24			; starting raster number
	DPB 2,[140400,,LNP]		; zap in low 4 bits of address
	LSH 2,-4			; throw low bits away
	DPB 2,[240500,,LNP]		; high 5 bits of address
	UPGIOT LDISP			; display the line
	POPJ 17,
; DMCHAR DMOUT Datamedia output routines ;⊗ DMCHAR DMOUT

; Character output to DM

DMCHAR:	SOSG DMCNT			; any room in buffer?
	 PUSHJ 17,DMOUT
	IDPB DMPNT			; save character
	POPJ 17,

; Buffer output to DM; called when DM buffer full or want to force buffer out

↑DMOUT:	SKIPN DMPGM			; any program there?
	 POPJ 17,
	HRRZ DMPNT			; get current value of pointer
	SUBI DMPGM-1			; compute number of words used
	MOVEM DMDISP+1			; set number of words to do
	UPGIOT DMDISP			; output DM program
	MOVS HPOS
	HRR VPOS
	ADDI 2-1			; two lines for who line
	CURSOR				; bop the cursor to last position
	SETZM DMPGM
	MOVE [DMPGM,,DMPGM+1]
	BLT DMPGM+DMBUFL-1		; clear the old program
	MOVEI <5*DMBUFL>-4
	MOVEM DMCNT			; initialize DM counter
	MOVE [440700,,DMPGM]
	MOVEM DMPNT			; initialize DM pointer
	MOVEI 177			; quote
	IDPB DMPNT
	MOVEI 14			; cursor position
	IDPB DMPNT
	MOVE HPOS			; horizontal position
	XORI 140
	IDPB DMPNT
	MOVE VPOS			; vertical position
	ADDI 2				; who line space
	XORI 140
	IDPB DMPNT
	POPJ 17,

	BEND DISPLY